package com.rocky.component.elasticsearch;

import com.rocky.component.util.ObjectMapperUtil;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.MatchPhrasePrefixQueryBuilder;
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.elasticsearch.index.query.SimpleQueryStringBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * @author rocky
 * date 2025-07-17
 * Description:
 */
@RestController
@RequestMapping("/fullTextQuery")
public class FullTextQueryExample {

    private final RestHighLevelClient restHighLevelClient;

    public FullTextQueryExample(RestHighLevelClient restHighLevelClient) {
        this.restHighLevelClient = restHighLevelClient;
    }

    @GetMapping("/matchQuery")
    public String matchQuery() throws IOException {
        // match
        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("address", "mill lane")
                .fuzziness(Fuzziness.AUTO);  // fuzziness 模糊匹配。AUTO：0-2 允许差异0  3-5 允许差异1  5+ 允许差异2

        return search(queryBuilder);
    }

    @GetMapping("/matchPhraseQuery")
    public String matchPhraseQuery() throws IOException {
        // match_phrase
        MatchPhraseQueryBuilder queryBuilder = QueryBuilders.matchPhraseQuery("address", "mill lane")
                .slop(1);   // slop 模糊顺序。允许语句的最大间隔

        return search(queryBuilder);
    }

    @GetMapping("/matchPhrasePrefixQuery")
    public String matchPhrasePrefixQuery() throws IOException {
        // match_phrase_prefix
        MatchPhrasePrefixQueryBuilder queryBuilder = QueryBuilders.matchPhrasePrefixQuery("address", "mill l");

        return search(queryBuilder);
    }

    @GetMapping("/multiMatchQuery")
    public String multiMatchQuery() throws IOException {
        // multi_match
        MultiMatchQueryBuilder queryBuilder = QueryBuilders.multiMatchQuery("Thompson Key", "firstname", "lastname")
                .minimumShouldMatch("20%")  // 代表文档需要匹配的 should 子句的最小比例
                .tieBreaker(0.2f)   // 分数权重比
                .type(MultiMatchQueryBuilder.Type.BEST_FIELDS); // 默认类型。此类型会在每个字段中独立执行查询，然后选择每个文档在所有字段中得分最高的那个字段的得分作为该文档的最终得分
//                .type(MultiMatchQueryBuilder.Type.MOST_FIELDS); // 该类型同样会在每个字段中独立执行查询，但文档的最终得分是所有匹配字段得分的总和。这种类型有助于提高召回率，因为它会综合考虑多个字段的匹配情况
//                .type(MultiMatchQueryBuilder.Type.CROSS_FIELDS);    // 该类型会将所有指定字段视为一个大的字段进行查询，它会跨字段匹配词项，适用于需要在多个字段中查找相关词组合的场景
//                .type(MultiMatchQueryBuilder.Type.PHRASE);  // 该类型要求查询字符串中的词必须按照指定顺序连续出现在文档的某个字段中，就像短语查询一样。
//                .type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX);   //  该类型与 phrase 类似，但允许短语的最后一个词进行前缀匹配
//                .type(MultiMatchQueryBuilder.Type.BOOL_PREFIX); // 对于除最后一个词条之外的其他词条，会构建布尔查询的 must 子句，这意味着文档必须包含这些词条

        return search(queryBuilder);
    }

    @GetMapping("/queryStringQuery")
    public String queryStringQuery() throws IOException {
        // query_string
        QueryStringQueryBuilder queryBuilder = QueryBuilders.queryStringQuery("White address:Terrace")
                .defaultField("lastname");  // 未指定查询字段，则使用该默认字段查询

        return search(queryBuilder);
    }

    @GetMapping("/simpleQueryStringQuery")
    public String simpleQueryStringQuery() throws IOException {
        // simple_query_string
        SimpleQueryStringBuilder queryBuilder = QueryBuilders.simpleQueryStringQuery("Terrace White")
                .field("firstname")
                .defaultOperator(Operator.OR);  // 默认操作逻辑符

        return search(queryBuilder);
    }

    private String search(QueryBuilder queryBuilder) throws IOException {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(queryBuilder);

        SearchRequest searchRequest = new SearchRequest("bank")
                .source(searchSourceBuilder);

        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        return ObjectMapperUtil.get().writeValueAsString(searchResponse.getHits());
    }
}
